[DX-155]- Unifies start and end params for history commands#150
[DX-155]- Unifies start and end params for history commands#150umair-ably merged 7 commits intomainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughIntroduces flexible time-range flags and a parseTimestamp utility, broad refactor of CLI flag sets (productApiFlags/clientIdFlag), consistent output helpers (progress/resource/success), JSON-aware error/output handling, duration flags for long-running commands, debug log removals, and expanded tests for time parsing and histories. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI
participant TimeUtil as parseTimestamp()
participant API as ControlAPI
participant Output
User->>CLI: run command with --start/--end
CLI->>TimeUtil: parseTimestamp(start) / parseTimestamp(end)
TimeUtil-->>CLI: startMs / endMs
CLI->>API: request history(stats) with startMs/endMs
API-->>CLI: history data (timestamps, items)
CLI->>Output: formatTimestamp(items/*.timestamp) + resource/progress wrappers
Output-->>User: rendered output (JSON or human)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Unifies --start/--end time range handling across history/stats CLI commands by introducing shared timeRangeFlags and a new parseTimestamp() utility (ISO 8601 / Unix ms / relative). Also standardizes output formatting and JSON guarding across many commands, while removing debug artifacts and restoring/adding missing flags (e.g., clientIdFlag, duration).
Changes:
- Added
src/utils/time.ts(parseTimestamp) and updated commands to use sharedtimeRangeFlagsfor consistent time parsing. - Standardized output helpers (
progress/success/resource), improved JSON guarding, and removed debug logging artifacts. - Added/adjusted tests to cover flexible timestamps and updated output expectations.
Reviewed changes
Copilot reviewed 80 out of 80 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| test/unit/utils/time.test.ts | Adds unit coverage for parseTimestamp() (ms, ISO, relative, errors). |
| test/unit/commands/stats/app.test.ts | Verifies stats:app accepts ISO/relative/Unix ms for time flags. |
| test/unit/commands/stats/account.test.ts | Verifies stats:account accepts ISO/relative/Unix ms for time flags. |
| test/unit/commands/rooms/messages.test.ts | Adds coverage for rooms messages history time range parsing. |
| test/unit/commands/queues/delete.test.ts | Updates expectations to match new standardized output. |
| test/unit/commands/interactive.test.ts | Removes stderr debug logging in tests. |
| test/unit/commands/interactive-sigint.test.ts | Renames unused vars and adjusts timeout fallback behavior. |
| test/unit/commands/did-you-mean.test.ts | Removes CI debug output and cleans variable usage. |
| test/unit/commands/channels/history.test.ts | Adds coverage for start/end parsing (Unix ms / relative). |
| test/unit/commands/channels/batch-publish.test.ts | Updates JSON-mode parsing and output assertions to match new guards/helpers. |
| test/unit/commands/apps/set-apns-p12.test.ts | Updates success message expectation. |
| test/e2e/spaces/spaces-e2e.test.ts | Removes conditional debug output in polling loop. |
| test/e2e/interactive/ctrl-c-behavior.test.ts | Removes DEBUG_TEST logging and console error artifacts. |
| src/utils/time.ts | Introduces parseTimestamp() supporting ISO/Unix ms/relative shorthand. |
| src/services/history-manager.ts | Removes debug-only logging; keeps silent failure behavior. |
| src/flags.ts | Adds shared timeRangeFlags for --start/--end. |
| src/commands/support/ask.ts | Adds JSON output for successful responses and adjusts spinner behavior. |
| src/commands/stats/app.ts | Switches to timeRangeFlags + parseTimestamp; standardizes progress output. |
| src/commands/stats/account.ts | Switches to timeRangeFlags + parseTimestamp; standardizes progress output. |
| src/commands/spaces/members/subscribe.ts | Restores product flags/clientId flag; adds duration flag; improves JSON error payload. |
| src/commands/spaces/members/enter.ts | Restores product flags/clientId flag and updates duration description. |
| src/commands/spaces/locks/subscribe.ts | Restores product flags/clientId flag; adds duration flag; adjusts output + JSON errors. |
| src/commands/spaces/locks/get.ts | Restores product flags/clientId flag and cleans legacy comments/strings. |
| src/commands/spaces/locks/get-all.ts | Restores product flags/clientId flag. |
| src/commands/spaces/locks/acquire.ts | Restores product flags/clientId flag; adds duration flag. |
| src/commands/spaces/locations/subscribe.ts | Restores product flags/clientId flag; updates error output routing. |
| src/commands/spaces/locations/set.ts | Restores product flags/clientId flag; replaces process.exit with this.exit. |
| src/commands/spaces/locations/get-all.ts | Restores product flags/clientId flag; refactors location extraction; adjusts error handling. |
| src/commands/spaces/locations.ts | Uses product flags and adds override markers; removes unused parse result. |
| src/commands/spaces/list.ts | Uses product flags (instead of base global flags). |
| src/commands/spaces/cursors/subscribe.ts | Restores product flags/clientId flag; adds duration; JSON guards readiness signal. |
| src/commands/spaces/cursors/set.ts | Restores product flags/clientId flag; adds duration; punctuation consistency. |
| src/commands/spaces/cursors/get-all.ts | Restores product flags/clientId flag; changes output style; uses this.error on failures. |
| src/commands/rooms/typing/subscribe.ts | Uses product flags; updates duration description. |
| src/commands/rooms/typing/keystroke.ts | Uses product flags; adds duration flag. |
| src/commands/rooms/reactions/subscribe.ts | Uses product flags; updates duration description. |
| src/commands/rooms/reactions/send.ts | Uses product flags. |
| src/commands/rooms/presence/subscribe.ts | Uses product flags and restores clientIdFlag; updates duration description. |
| src/commands/rooms/presence/enter.ts | Uses product flags and restores clientIdFlag; removes early readiness log. |
| src/commands/rooms/occupancy/subscribe.ts | Uses product flags; adds override markers; updates duration description. |
| src/commands/rooms/occupancy/get.ts | Uses product flags; output formatting via resource(). |
| src/commands/rooms/messages/subscribe.ts | Uses product flags + clientIdFlag; standardizes progress and resource formatting. |
| src/commands/rooms/messages/send.ts | Uses product flags + clientIdFlag; progress helper usage. |
| src/commands/rooms/messages/reactions/subscribe.ts | Uses product flags; removes unused flags parameters. |
| src/commands/rooms/messages/reactions/send.ts | Uses product flags; punctuation consistency. |
| src/commands/rooms/messages/reactions/remove.ts | Uses product flags; punctuation consistency. |
| src/commands/rooms/messages/history.ts | Uses timeRangeFlags + parseTimestamp; standardizes output. |
| src/commands/rooms/list.ts | Uses product flags; uses resource() for room names/counts. |
| src/commands/queues/delete.ts | Skips confirmation in JSON mode; emits structured JSON success payload. |
| src/commands/logs/subscribe.ts | Standardizes duration description; removes unused fields. |
| src/commands/logs/push/subscribe.ts | Adds duration; replaces manual signal handling with wait helper. |
| src/commands/logs/push/history.ts | Adds timeRangeFlags + parseTimestamp; updates examples. |
| src/commands/logs/history.ts | Adds timeRangeFlags + parseTimestamp; uses formatTimestamp output convention. |
| src/commands/logs/connection-lifecycle/subscribe.ts | Standardizes duration/rewind descriptions; removes unused cleanup flag. |
| src/commands/logs/connection-lifecycle/history.ts | Adds timeRangeFlags + parseTimestamp; uses formatTimestamp. |
| src/commands/logs/channel-lifecycle/subscribe.ts | Adds duration flag and uses it in wait helper. |
| src/commands/interactive.ts | Removes ABLY_DEBUG_KEYS debug logging blocks. |
| src/commands/integrations/delete.ts | Skips confirmation in JSON mode; emits structured JSON success payload. |
| src/commands/channels/subscribe.ts | JSON-guards readiness signal; adjusts parsing variable usage. |
| src/commands/channels/publish.ts | Uses progress() helper for multi-publish progress text. |
| src/commands/channels/presence/subscribe.ts | Changes secondary labels to chalk.dim. |
| src/commands/channels/presence/enter.ts | Changes secondary labels to chalk.dim. |
| src/commands/channels/occupancy/subscribe.ts | Changes secondary labels to chalk.dim. |
| src/commands/channels/occupancy/get.ts | Adds override markers; improves label formatting with chalk.dim. |
| src/commands/channels/occupancy.ts | Adds override markers on static fields. |
| src/commands/channels/list.ts | Uses resource() for channel names. |
| src/commands/channels/history.ts | Adds timeRangeFlags + parseTimestamp; uses formatTimestamp. |
| src/commands/channels/batch-publish.ts | Standardizes progress/success output; improves per-channel result formatting. |
| src/commands/bench/subscriber.ts | Standardizes duration description. |
| src/commands/auth/keys/create.ts | Uses progress() helper for creation message. |
| src/commands/apps/update.ts | Uses progress() helper and resource() for app id. |
| src/commands/apps/set-apns-p12.ts | Uses progress()/success() helpers; updates success message. |
| src/commands/apps/delete.ts | Uses progress() helper and resource() for app id. |
| src/commands/apps/create.ts | Uses progress() helper and resource() for app name. |
| src/commands/apps/channel-rules/list.ts | Uses ControlBaseCommand.globalFlags (correct flag set). |
| src/commands/apps/channel-rules/delete.ts | Fixes shadowed appId variable initialization. |
| src/commands/apps/channel-rules/create.ts | Fixes shadowed appId variable initialization. |
| src/commands/accounts/login.ts | Uses progress() helper for browser/app creation messaging. |
| .cursor/rules/AI-Assistance.mdc | Documents new output + flag conventions (duration, timeRangeFlags, JSON errors). |
| .claude/CLAUDE.md | Documents flag categories and JSON/error/output conventions including timeRangeFlags. |
Comments suppressed due to low confidence (1)
src/commands/support/ask.ts:148
- In the catch block,
--jsonmode still logs a human-readable failure message (including chalk styling) and then callsthis.error(), which will pollute JSON output. Prefer usingthis.jsonError({ error: errorMsg, success: false }, flags)(or equivalent) whenshouldOutputJson(flags)is true, and avoid any non-JSON logging in that branch so JSON consumers get a clean error object plus a non-zero exit code.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Further reinforces rules introduced in previous unification efforts
0794c34 to
3f04ce9
Compare
There was a problem hiding this comment.
Actionable comments posted: 10
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
test/unit/commands/queues/delete.test.ts (1)
47-103:⚠️ Potential issue | 🟠 MajorAdd tests for the new
--jsondeletion pathThe command now has JSON-mode specific behavior (structured success payload and no interactive confirmation), but this suite only validates human-output deletion text. Please add a focused
--jsonsuccess test (and ideally a non---force+--jsoncase proving no prompt path).As per coding guidelines: "Update or add tests when new features or changes are made, ensuring all tests pass before deeming work complete."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/unit/commands/queues/delete.test.ts` around lines 47 - 103, Add tests covering the new --json path for queues:delete: create one test that calls runCommand(["queues:delete", mockQueueId, "--force", "--json"], ...) (and the equivalent custom-app-id variant) mocking the same GET /v1/apps/... and DELETE endpoints with nock, then parse stdout as JSON and assert the structured success payload (e.g., contains a success flag/queue id/message) and that no interactive prompt text is present; also add a second test that runs runCommand(["queues:delete", mockQueueId, "--json"], ...) without --force to confirm JSON-mode bypasses interactive confirmation (mock any confirmation GETs if present) and still returns the structured JSON success response. Ensure tests reuse existing helpers like getMockConfigManager(), createMockQueue(), and runCommand to mirror the other tests.src/commands/spaces/cursors/set.ts (1)
68-70:⚠️ Potential issue | 🟠 Major
--durationcontract is inconsistent with command guidelines
--durationcurrently behaves as “0 = immediate exit” in runtime logic, but command guidelines require “0 = run indefinitely”. Please align both description and behavior.Proposed fix
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),- // Decide how long to remain connected - if (flags.duration === 0) { - // Give Ably a moment to propagate the cursor update before exiting so that - // subscribers in automated tests have a chance to receive the event. - await new Promise((resolve) => setTimeout(resolve, 600)); - - // In immediate exit mode, we don't keep the process alive beyond this. - this.exit(0); - } + // Decide how long to remain connected (0 = run indefinitely) + const waitDuration = flags.duration === 0 ? undefined : flags.duration; ... - const exitReason = await waitUntilInterruptedOrTimeout(flags.duration); + const exitReason = await waitUntilInterruptedOrTimeout(waitDuration);As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
Also applies to: 382-389
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/cursors/set.ts` around lines 68 - 70, Update the duration flag declaration and runtime handling so the flag description and behavior match the command guidelines: change the Flags.integer({ description: ... }) for the "duration" flag to "Automatically exit after N seconds (0 = run indefinitely)" and ensure its alias is char: "D"; then modify any runtime logic that currently treats duration === 0 as "immediate exit" to instead treat 0 as "run indefinitely" (i.e., skip applying a timeout when duration is 0). Apply the same description and behavior change to the other duration flag declaration found around the second occurrence (lines ~382-389) so both definitions and their consumers consistently use 0 = run indefinitely.src/commands/rooms/reactions/subscribe.ts (1)
32-39:⚠️ Potential issue | 🟡 MinorMissing
clientIdFlagfor subscribe command.This command creates a realtime connection via
createChatClient()and attaches to a room. Per the flag architecture guidelines, commands that create a realtime client or join a channel should includeclientIdFlagto allow users to supply a client identity.Proposed fix
import { ChatBaseCommand } from "../../../chat-base-command.js"; -import { productApiFlags } from "../../../flags.js"; +import { productApiFlags, clientIdFlag } from "../../../flags.js"; import { waitUntilInterruptedOrTimeout } from "../../../utils/long-running.js";static override flags = { ...productApiFlags, + ...clientIdFlag, duration: Flags.integer({Based on learnings: "The clientIdFlag (--client-id) should be available on commands where a client identity is meaningful... ensure commands that perform identifiable actions... or otherwise require a client identity expose the flag consistently."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/rooms/reactions/subscribe.ts` around lines 32 - 39, The subscribe command's static flags (in the class in subscribe.ts) currently only merges productApiFlags and duration; add the shared clientIdFlag to these flags so users can pass --client-id when the command calls createChatClient() and joins a room. Update the flags block (the static override flags in the Subscribe command) to include clientIdFlag alongside productApiFlags and duration, and ensure any import or reference to clientIdFlag is present so the flag is recognized.src/commands/spaces/locations/subscribe.ts (1)
390-403:⚠️ Potential issue | 🟠 MajorFail fast when subscription setup fails
If
this.space.locations.subscribe(...)throws, non-JSON mode currently only logs and then continues to wait, resulting in a hanging command with no active subscription.Suggested fix
} catch (error) { const errorMsg = `Error subscribing to location updates: ${error instanceof Error ? error.message : String(error)}`; this.logCliEvent(flags, "location", "subscribeError", errorMsg, { error: errorMsg, spaceName, }); if (this.shouldOutputJson(flags)) { this.jsonError( { error: errorMsg, spaceName, status: "error", success: false }, flags, ); } else { - this.logToStderr(errorMsg); + this.error(errorMsg); } + return; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/locations/subscribe.ts` around lines 390 - 403, The catch block around this.space.locations.subscribe currently logs the error (via this.logCliEvent and either this.jsonError or this.logToStderr) but does not terminate, causing the CLI to hang; update the catch to fail fast by exiting or throwing after logging—e.g., after calling this.jsonError or this.logToStderr, call this.exit(1) or rethrow the error so the command stops. Ensure you modify the catch that wraps this.space.locations.subscribe and keep the existing logging calls (this.logCliEvent, this.jsonError, this.logToStderr) unchanged, only adding the exit/throw to terminate execution.
🟡 Minor comments (26)
src/commands/channels/presence/enter.ts-43-46 (1)
43-46:⚠️ Potential issue | 🟡 MinorRestore the canonical
--durationdescription textThe new description omits the
0behavior, which makes CLI semantics less clear. Please keep the standard wording.Suggested patch
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/channels/presence/enter.ts` around lines 43 - 46, Update the duration flag description to the canonical text: change the Flags.integer option for the duration flag (symbol: duration) so its description reads "Automatically exit after N seconds (0 = run indefinitely)" and ensure the alias remains char: "D" (i.e., restore the original wording while leaving required: false and other options unchanged).src/commands/apps/set-apns-p12.ts-57-59 (1)
57-59:⚠️ Potential issue | 🟡 MinorProgress message should end with
....As per coding guidelines, progress messages must end with
...to indicate an in-progress action.Proposed fix
this.log( - progress(`Uploading APNS P12 certificate for app ${resource(args.id)}`), + progress(`Uploading APNS P12 certificate for app ${resource(args.id)}...`), );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/apps/set-apns-p12.ts` around lines 57 - 59, Update the progress message passed to progress(...) so it ends with an ellipsis; specifically modify the call in set-apns-p12 where this.log(progress(`Uploading APNS P12 certificate for app ${resource(args.id)}`)) is used to append "..." to the string so the progress output follows the guideline (i.e., ensure the template literal given to progress includes trailing "...").src/commands/channels/subscribe.ts-63-65 (1)
63-65:⚠️ Potential issue | 🟡 MinorRestore the standard
--durationhelp text.The description should include the indefinite-run behavior to match command help consistency.
Suggested patch
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/channels/subscribe.ts` around lines 63 - 65, Update the duration flag's description in src/commands/channels/subscribe.ts: change the Flags.integer({ description: ... , char: "D" }) entry so the description reads "Automatically exit after N seconds (0 = run indefinitely)" and ensure the alias remains char: "D" to match the standard help text for the --duration/-D flag.src/services/history-manager.ts-40-43 (1)
40-43:⚠️ Potential issue | 🟡 MinorEmpty
catchblock violates ESLintno-emptyrule.The catch block at lines 40–43 contains only comments with no statements. ESLint 9's recommended rules enable
no-emptyby default; this will fail linting. Add a no-op statement to satisfy the rule while preserving the intended error-suppressing behavior.Suggested fix
} catch (error) { void error; // Silently ignore history load errors // History is a nice-to-have feature, shouldn't break the shell }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/services/history-manager.ts` around lines 40 - 43, The empty catch block in history-manager.ts (the catch following the try that loads history, e.g., in the loadHistory routine) violates ESLint no-empty; add a no-op statement inside that catch (for example a void 0 expression statement) so the block is not empty but preserves the silent error-suppressing behavior—do not change error handling semantics or add throwing behavior.src/commands/integrations/delete.ts-99-103 (1)
99-103:⚠️ Potential issue | 🟡 MinorUse dim styling for secondary field labels in the detail block.
Lines 99-103 currently print plain labels; they should use
chalk.dim()to match the pattern used throughout the CLI for secondary labels in structured output.Proposed fix
+import chalk from "chalk"; import { Args, Flags } from "@oclif/core"; @@ - this.log(`ID: ${integration.id}`); - this.log(`App ID: ${integration.appId}`); - this.log(`Type: ${integration.ruleType}`); - this.log(`Source Type: ${integration.source.type}`); + this.log(`${chalk.dim("ID:")} ${integration.id}`); + this.log(`${chalk.dim("App ID:")} ${integration.appId}`); + this.log(`${chalk.dim("Type:")} ${integration.ruleType}`); + this.log(`${chalk.dim("Source Type:")} ${integration.source.type}`);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/integrations/delete.ts` around lines 99 - 103, The printed detail labels for integration fields (ID, App ID, Type, Source Type) are plain text; update the output to use chalk.dim() for secondary labels to match CLI styling—wrap the label portions passed to this.log (the strings like "ID:", "App ID:", "Type:", "Source Type:") with chalk.dim() where the integration details are printed (the block referencing integration.id, integration.appId, integration.ruleType, integration.source.type), so each line becomes a dimmed label followed by the value.src/commands/channels/presence/subscribe.ts-38-42 (1)
38-42:⚠️ Potential issue | 🟡 MinorDuration flag description doesn't match coding guidelines.
The description was changed to
"Automatically exit after N seconds"but the coding guidelines specify the exact wording should be"Automatically exit after N seconds (0 = run indefinitely)"with alias-D. The(0 = run indefinitely)part helps users understand that0or omitting the flag means indefinite execution.🔧 Suggested fix
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/channels/presence/subscribe.ts` around lines 38 - 42, Update the duration flag definition in subscribe.ts (the Flags.integer call for the duration flag) to use the exact description "Automatically exit after N seconds (0 = run indefinitely)" and ensure the short alias remains -D (char: "D") so the flag matches the coding guideline.src/commands/logs/channel-lifecycle/subscribe.ts-27-31 (1)
27-31:⚠️ Potential issue | 🟡 MinorUse the standardized
--durationdescription textPlease update the description to include
"(0 = run indefinitely)"for consistency with command guidelines.Suggested fix
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines, "
--durationflag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/logs/channel-lifecycle/subscribe.ts` around lines 27 - 31, Update the duration flag definition used in subscribe.ts so its description matches the standard text: change the Flags.integer({ description: "Automatically exit after N seconds" ... }) to use "Automatically exit after N seconds (0 = run indefinitely)" and keep the alias char: "D" and required: false on the same Flags.integer call for the duration flag.src/commands/channels/occupancy/subscribe.ts-36-40 (1)
36-40:⚠️ Potential issue | 🟡 MinorAlign
--durationdescription with the command guideline
durationuses-Dcorrectly, but the description is missing the required"(0 = run indefinitely)"suffix.Suggested fix
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines, "
--durationflag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/channels/occupancy/subscribe.ts` around lines 36 - 40, The duration flag in src/commands/channels/occupancy/subscribe.ts uses Flags.integer but its description is missing the required suffix; update the description for the duration flag (the Flags.integer call named duration) to read "Automatically exit after N seconds (0 = run indefinitely)" and keep the existing alias char: "D".src/commands/accounts/login.ts-230-230 (1)
230-230:⚠️ Potential issue | 🟡 MinorAvoid colored resource text inside
progress()action text
progress()action text should stay uncolored;resource(appName)introduces color here.Suggested fix
- this.log(`\n${progress(`Creating app ${resource(appName)}`)}`); + this.log(`\n${progress(`Creating app ${appName}`)}`);As per coding guidelines, "Use progress() helper for in-progress actions (no color on action text, ends with '...')."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/accounts/login.ts` at line 230, The progress action text currently includes colored output via resource(appName) inside the progress(...) call in the this.log(...) statement; replace the colored helper with plain appName (or a stripped/colorless version) and ensure the progress text ends with '...' so use progress(`Creating app ${appName}...`) (or progress(`Creating app ${stripColor(appName)}...`) if you need to strip color elsewhere) instead of progress(`Creating app ${resource(appName)}`).src/commands/bench/subscriber.ts-36-39 (1)
36-39:⚠️ Potential issue | 🟡 MinorAlign
--durationdescription with the standard textPlease include the
(0 = run indefinitely)suffix in the help text for consistency.As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/bench/subscriber.ts` around lines 36 - 39, Update the duration flag's description on the Flags.integer call for `duration` (the flag with char "D") to match the standard text: change the description to "Automatically exit after N seconds (0 = run indefinitely)". Ensure you only modify the description string for the `duration` flag in the subscriber command where `duration: Flags.integer({ char: "D", ... })` is defined.src/commands/rooms/presence/enter.ts-46-48 (1)
46-48:⚠️ Potential issue | 🟡 MinorUse the standard
--durationdescription textLine 47 should include the indefinite-run hint for consistency across long-running commands.
As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/rooms/presence/enter.ts` around lines 46 - 48, Update the duration flag declaration in enter.ts so its description matches the standard text and alias: change the Flags.integer description to "Automatically exit after N seconds (0 = run indefinitely)" and ensure the char/alias is set to "D" (refer to the duration flag definition in this file).src/commands/logs/subscribe.ts-29-31 (1)
29-31:⚠️ Potential issue | 🟡 MinorRestore the standard
--durationhelp textLine 30 should include the indefinite-run note to match command help consistency.
Suggested fix
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/logs/subscribe.ts` around lines 29 - 31, Update the Flags.integer definition for the duration flag in subscribe.ts (the duration flag declaration) to restore the standard help text: set its description to "Automatically exit after N seconds (0 = run indefinitely)" and ensure the char alias remains "D" so the --duration flag matches the project guideline and help output.src/commands/rooms/typing/subscribe.ts-30-33 (1)
30-33:⚠️ Potential issue | 🟡 MinorAlign
--durationdescription with the shared command contract.Line 31 should include the indefinite-run note for consistency across long-running commands.
Suggested fix
- description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)",As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/rooms/typing/subscribe.ts` around lines 30 - 33, The --duration flag in the subscribe command currently has an incomplete description; update the duration Flags.integer declaration (the duration flag in src/commands/rooms/typing/subscribe.ts) so its description reads "Automatically exit after N seconds (0 = run indefinitely)" and ensure the char alias is '-D' (char: "D") to match the shared long-running command contract.src/commands/logs/connection-lifecycle/subscribe.ts-22-24 (1)
22-24:⚠️ Potential issue | 🟡 MinorUse the standard
--durationdescription text.Line 23 should include the standard suffix for indefinite mode to match command consistency.
Suggested fix
- description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)",As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/logs/connection-lifecycle/subscribe.ts` around lines 22 - 24, Update the duration flag description to the standard text and keep the alias; in the Flags.integer call for the duration flag in subscribe.ts (the duration: Flags.integer({...}) object), change description to "Automatically exit after N seconds (0 = run indefinitely)" and ensure the char remains "D" so the flag matches the project's standard --duration wording and alias.src/commands/logs/push/subscribe.ts-27-31 (1)
27-31:⚠️ Potential issue | 🟡 MinorUse the standard
--durationhelp textKeep the duration description consistent with other commands and docs.
Suggested change
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/logs/push/subscribe.ts` around lines 27 - 31, The duration flag definition (duration: Flags.integer) uses a non-standard description; update its description to "Automatically exit after N seconds (0 = run indefinitely)" and ensure the alias/char remains "D" and required stays false so it matches other commands and docs (change only the description string in the duration: Flags.integer declaration).src/commands/spaces/members/subscribe.ts-31-34 (1)
31-34:⚠️ Potential issue | 🟡 MinorNormalize
--durationdescription textPlease use the shared duration wording used across long-running commands.
Suggested change
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/members/subscribe.ts` around lines 31 - 34, Update the duration flag in the subscribe command so its description uses the shared wording and alias: change the description string in the duration: Flags.integer({ ... }) definition inside src/commands/spaces/members/subscribe.ts to "Automatically exit after N seconds (0 = run indefinitely)" and ensure the flag has char: "D" set (alias -D) and required: false stays unchanged.src/commands/rooms/messages/subscribe.ts-65-67 (1)
65-67:⚠️ Potential issue | 🟡 MinorAlign
--durationdescription with the CLI standardThe description should include the indefinite-run behavior note for consistency across long-running commands.
Suggested change
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/rooms/messages/subscribe.ts` around lines 65 - 67, Update the duration flag metadata in the subscribe command: change the Flags.integer description for the duration flag to "Automatically exit after N seconds (0 = run indefinitely)" and ensure its alias remains char: "D" (the duration flag definition using Flags.integer in subscribe.ts).src/commands/spaces/cursors/subscribe.ts-36-38 (1)
36-38:⚠️ Potential issue | 🟡 MinorUse the standard help text for
--duration.Line 37 should include
(0 = run indefinitely)for consistency across long-running commands.Proposed fix
- description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)",As per coding guidelines
src/commands/**/*.ts: The--durationflag should have description'Automatically exit after N seconds (0 = run indefinitely)'with alias-D.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/cursors/subscribe.ts` around lines 36 - 38, The duration flag's help text and alias need to follow the standard: update the Flags.integer declaration for the duration flag (the symbol named duration in subscribe.ts) so its description reads "Automatically exit after N seconds (0 = run indefinitely)" and ensure the single-character alias is "D" (i.e., char: "D").src/commands/spaces/locks/acquire.ts-36-39 (1)
36-39:⚠️ Potential issue | 🟡 MinorStandardize the
--durationdescription text.Line 37 should include the
(0 = run indefinitely)suffix to match CLI conventions.Proposed fix
- description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)",As per coding guidelines
src/commands/**/*.ts: The--durationflag should have description'Automatically exit after N seconds (0 = run indefinitely)'with alias-D.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/locks/acquire.ts` around lines 36 - 39, Update the duration flag's description in the Flags.integer call for the duration option so it reads "Automatically exit after N seconds (0 = run indefinitely)"; ensure this change is applied inside the duration: Flags.integer({ ... }) block and retain the alias char: "D" so the flag remains available as -D..cursor/rules/AI-Assistance.mdc-135-135 (1)
135-135:⚠️ Potential issue | 🟡 MinorKeep the rule file consistent with the enforced
--durationconvention.Line 135 currently contradicts the command guideline and will propagate inconsistent help text.
Proposed fix
-- `--duration`: `"Automatically exit after N seconds"`, alias `-D` +- `--duration`: `"Automatically exit after N seconds (0 = run indefinitely)"`, alias `-D`As per coding guidelines
src/commands/**/*.ts: The--durationflag should have description'Automatically exit after N seconds (0 = run indefinitely)'with alias-D.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.cursor/rules/AI-Assistance.mdc at line 135, Update the `--duration` flag entry in the AI-Assistance.mdc rule file so it matches the enforced convention: change its description to 'Automatically exit after N seconds (0 = run indefinitely)' and ensure the alias is `-D` for the `--duration` flag (look for the `--duration` token on line containing the flag description to locate the entry).src/commands/rooms/typing/keystroke.ts-43-47 (1)
43-47:⚠️ Potential issue | 🟡 MinorAlign
--durationhelp text with the command convention.Line 44 omits the
(0 = run indefinitely)suffix used across commands.Proposed fix
- description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)",As per coding guidelines
src/commands/**/*.ts: The--durationflag should have description'Automatically exit after N seconds (0 = run indefinitely)'with alias-D.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/rooms/typing/keystroke.ts` around lines 43 - 47, Update the duration flag's description to match the project convention: change the Flags.integer description for the duration flag (the property named duration using Flags.integer) to "Automatically exit after N seconds (0 = run indefinitely)"; keep the alias/char as "D" unchanged so the flag remains available as -D.src/commands/rooms/occupancy/subscribe.ts-45-47 (1)
45-47:⚠️ Potential issue | 🟡 MinorUse the standard
--durationdescription string.Line 46 should include the indefinite-run note for consistency with other long-running commands.
Proposed fix
- description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)",As per coding guidelines
src/commands/**/*.ts: The--durationflag should have description'Automatically exit after N seconds (0 = run indefinitely)'with alias-D.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/rooms/occupancy/subscribe.ts` around lines 45 - 47, Update the duration flag in src/commands/rooms/occupancy/subscribe.ts: modify the Flags.integer configuration for the "duration" flag (symbol: duration, char: "D") so its description exactly reads "Automatically exit after N seconds (0 = run indefinitely)" to match the standard long-running command phrasing and retain the alias -D.src/commands/spaces/locations/subscribe.ts-55-57 (1)
55-57:⚠️ Potential issue | 🟡 MinorRestore the standard
--durationhelp textThe current description drops the documented
0semantics.Suggested fix
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/locations/subscribe.ts` around lines 55 - 57, Update the duration flag definition in subscribe.ts (the duration: Flags.integer({...}) entry) to restore the standard help text: set description to "Automatically exit after N seconds (0 = run indefinitely)" and ensure the alias char remains "D" (i.e., preserve char: "D"); no other behavior changes required.src/commands/rooms/messages/reactions/subscribe.ts-47-49 (1)
47-49:⚠️ Potential issue | 🟡 MinorAlign
--durationdescription with command standardPlease include the
(0 = run indefinitely)behavior in the help text.As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/rooms/messages/reactions/subscribe.ts` around lines 47 - 49, Update the --duration flag description to include the "(0 = run indefinitely)" behavior; in the options definition for the duration flag (the object with description: "Automatically exit after N seconds", char: "D", required: false in subscribe.ts) replace the description string with "Automatically exit after N seconds (0 = run indefinitely)" and ensure the alias char remains "D".src/commands/spaces/locks/subscribe.ts-35-37 (1)
35-37:⚠️ Potential issue | 🟡 MinorUse the standard
--durationdescription textThe description should explicitly document the
0behavior for indefinite run.Suggested fix
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/locks/subscribe.ts` around lines 35 - 37, Update the duration flag declaration (duration: Flags.integer) to use the standard description text "Automatically exit after N seconds (0 = run indefinitely)" and ensure the alias char is '-D' (or char: "D") to match guidelines; locate the duration flag in subscribe.ts (the duration: Flags.integer({...}) block) and replace the current description with the exact required string.src/commands/rooms/messages/history.ts-92-94 (1)
92-94:⚠️ Potential issue | 🟡 MinorProgress text is inaccurate for
--order oldestFirstThe message always says “most recent messages,” which is misleading when users request oldest-first order.
Suggested wording
- `Fetching ${flags.limit} most recent messages from room ${resource(args.room)}`, + `Fetching up to ${flags.limit} messages from room ${resource(args.room)}`,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/rooms/messages/history.ts` around lines 92 - 94, Update the progress message in the call to progress(...) so it reflects the requested ordering: check the CLI flag used for ordering (e.g., flags.order or a boolean like flags.oldestFirst) and change the text from "Fetching ${flags.limit} most recent messages from room ${resource(args.room)}" to conditionally say "most recent messages" when order is newest-first and "oldest messages" (or "oldest messages first") when order is oldestFirst (i.e., when flags.order === 'oldestFirst' or flags.oldestFirst is true); keep using flags.limit and resource(args.room) and only alter the descriptive text.
🧹 Nitpick comments (7)
src/commands/support/ask.ts (1)
38-41: Use sharedprogress()output helper instead of raworaThis command is still using
ora(...)directly for in-progress output. Please switch to the sharedprogress()helper for consistent CLI output behavior across commands.As per coding guidelines "Use progress() helper for in-progress actions (no color on action text, ends with '...')".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/support/ask.ts` around lines 38 - 41, Replace the direct ora usage that sets spinner in the ask command with the shared progress() helper: instead of creating spinner via ora("Thinking...").start(), call progress("Thinking") when interactive output and JSON output are not enabled (same condition using isInteractive || this.shouldOutputJson(flags)), and store the returned progress handle in the same spinner variable so existing stop/succeed calls still work; ensure the action text passed to progress has no color and does not include the trailing ellipsis (progress will append "..." itself).test/unit/commands/stats/account.test.ts (1)
1-26: Use constructor-level Ably SDK stubbing for.test.tsThis file relies on network interception only. For unit-level isolation in this repo, please stub
Ably.Restconstructor with sinon and restore inafterEach.As per coding guidelines: "Mock the Ably SDK constructor (Ably.Rest) using sinon stubs, not just individual methods, and always restore stubs in afterEach".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/unit/commands/stats/account.test.ts` around lines 1 - 26, The test currently relies only on nock; instead stub the Ably SDK constructor Ably.Rest using sinon so the test is unit-scoped: import sinon and Ably, then in beforeEach create a sinon.stub(Ably, "Rest").callsFake(() => ({ /* return the minimal instance used by stats:account, e.g. stats: { get: sinon.stub().resolves(mockStats) } */ })); use that stubbed instance to satisfy the command run and keep the existing process.env setup; in afterEach restore the stub by calling sinon.restore() (or (Ably.Rest as any).restore()) and keep nock.cleanAll() so all stubs are removed between tests.test/unit/commands/rooms/messages.test.ts (1)
437-440: Harden the relative-time assertion against runtime jitter.This assertion can be flaky under slower CI timing. Consider validating against a captured before/after execution range instead of a single
Date.now()sample.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/unit/commands/rooms/messages.test.ts` around lines 437 - 440, Replace the single Date.now() sample check with a before/after range: capture const before = Date.now() before you call the code that triggers room.messages.history, and const after = Date.now() immediately after; then assert callArgs.start is >= before - 5000 and <= after + 5000 (or tighter) so the expectation around room.messages.history.mock.calls[0][0].start is tolerant to CI jitter.test/unit/commands/channels/history.test.ts (1)
191-195: Make relative-time assertions deterministic to reduce flaky CI runs.These checks rely on wall-clock timing with a fixed ±5s window. On slow runners, that can fail intermittently. Prefer asserting within a before/after window captured around command execution (or use a fixed clock).
Also applies to: 207-211
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/unit/commands/channels/history.test.ts` around lines 191 - 195, The relative-time assertions around channel.history.mock.calls[0][0].start are flaky because they use Date.now() directly; instead capture a deterministic before/after window around the command execution (e.g., const before = Date.now(); run the code that triggers channel.history(...); const after = Date.now();) and then assert callArgs.start is between before - tolerance and after + tolerance (or use a mocked/fixed clock). Apply the same change for the other similar assertions (the later block referencing channel.history.mock.calls and its start value) so both tests use captured before/after timestamps (or a fixed clock) rather than a single Date.now() comparison.src/commands/spaces/locks/acquire.ts (1)
46-51: Release infinallyshould be gated by successful acquisition.
this.lockIdis set from args before acquire, so Line 48 can attempt a release even when lock acquisition failed. Track acquisition state to avoid unnecessary release calls and noisy final-release errors.Proposed fix
export default class SpacesLocksAcquire extends SpacesBaseCommand { ... private lockId: null | string = null; + private lockAcquired = false; ... async finally(err: Error | undefined): Promise<void> { - if (this.lockId && this.space) { + if (this.lockAcquired && this.lockId && this.space) { try { await this.space.locks.release(this.lockId); } catch (error) { ... } } ... const lock = await this.space.locks.acquire( lockId, lockData as LockOptions, ); + this.lockAcquired = true;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/locks/acquire.ts` around lines 46 - 51, The finally block currently tries to release the lock whenever this.lockId is present, but this.lockId is populated from args before acquire and can cause release attempts when acquisition failed; introduce and set a boolean flag (e.g., this.lockAcquired or this.acquired) to true only after a successful call to the acquire logic, then change the finally check to require both this.lockId and the acquisition flag before calling this.space.locks.release(this.lockId) so release is only attempted for locks that were actually acquired.test/unit/commands/stats/app.test.ts (1)
33-36: Strengthen query assertions for time-range parsing.
query(true)only proves “some query exists”; it doesn’t verify that--start/--endwere parsed and sent correctly. Please assert expected query keys/format so timestamp parsing regressions fail.Suggested test hardening
- .query(true) + .query((queryObject) => { + // ISO / unix-ms paths should produce numeric ms values + return typeof queryObject.start === "string" && /^\d+$/.test(queryObject.start); + })- .query(true) + .query((queryObject) => { + // ISO test should include end as well + return ( + typeof queryObject.start === "string" && + /^\d+$/.test(queryObject.start) && + typeof queryObject.end === "string" && + /^\d+$/.test(queryObject.end) + ); + })As per coding guidelines
**/*.test.{ts,js}: Update or add Unit, Integration, and E2E tests as appropriate for your changes, following the testing strategy defined indocs/Testing.md.Also applies to: 55-58, 70-73
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/unit/commands/stats/app.test.ts` around lines 33 - 36, Replace the loose .query(true) nock matchers for the stats endpoint with specific assertions that validate the start/end query params and their format: locate the nock setup that creates scope from nock("https://control.ably.net").get(`/v1/apps/${appId}/stats`) and change .query(true) to either .query({ start: expectedStartIso, end: expectedEndIso }) when exact values are known, or .query(q => { return typeof q.start === 'string' && typeof q.end === 'string' && ISO_TIMESTAMP_REGEX.test(q.start) && ISO_TIMESTAMP_REGEX.test(q.end); }) to assert timestamp parsing; apply the same change to the other occurrences of .query(true) for stats tests so regressions in parsing --start/--end fail the tests.src/commands/spaces/locks/subscribe.ts (1)
277-277: Style event action labels consistentlyConsider highlighting
updatedas an event label usingchalk.yellow()for consistency with other event outputs.Suggested tweak
- `${formatTimestamp(timestamp)} Lock ${chalk.blue(lock.id)} updated`, + `${formatTimestamp(timestamp)} Lock ${chalk.blue(lock.id)} ${chalk.yellow("updated")}`,As per coding guidelines: "Use chalk.yellow() for event type labels in output".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/locks/subscribe.ts` at line 277, Change the event label styling so the word "updated" is highlighted with chalk.yellow() for consistency with other event outputs; locate the formatted message that uses formatTimestamp(timestamp) and chalk.blue(lock.id) (in subscribe.ts) and replace the plain "updated" label with chalk.yellow("updated") so the output reads `${formatTimestamp(timestamp)} Lock ${chalk.blue(lock.id)} ${chalk.yellow('updated')}` (ensure spacing/concatenation matches surrounding messages).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: a5a5c4e8-87a8-47a6-81d0-edb6648fdcb8
📒 Files selected for processing (80)
.claude/CLAUDE.md.cursor/rules/AI-Assistance.mdcsrc/commands/accounts/login.tssrc/commands/apps/channel-rules/create.tssrc/commands/apps/channel-rules/delete.tssrc/commands/apps/channel-rules/list.tssrc/commands/apps/create.tssrc/commands/apps/delete.tssrc/commands/apps/set-apns-p12.tssrc/commands/apps/update.tssrc/commands/auth/keys/create.tssrc/commands/bench/subscriber.tssrc/commands/channels/batch-publish.tssrc/commands/channels/history.tssrc/commands/channels/list.tssrc/commands/channels/occupancy.tssrc/commands/channels/occupancy/get.tssrc/commands/channels/occupancy/subscribe.tssrc/commands/channels/presence/enter.tssrc/commands/channels/presence/subscribe.tssrc/commands/channels/publish.tssrc/commands/channels/subscribe.tssrc/commands/integrations/delete.tssrc/commands/interactive.tssrc/commands/logs/channel-lifecycle/subscribe.tssrc/commands/logs/connection-lifecycle/history.tssrc/commands/logs/connection-lifecycle/subscribe.tssrc/commands/logs/history.tssrc/commands/logs/push/history.tssrc/commands/logs/push/subscribe.tssrc/commands/logs/subscribe.tssrc/commands/queues/delete.tssrc/commands/rooms/list.tssrc/commands/rooms/messages/history.tssrc/commands/rooms/messages/reactions/remove.tssrc/commands/rooms/messages/reactions/send.tssrc/commands/rooms/messages/reactions/subscribe.tssrc/commands/rooms/messages/send.tssrc/commands/rooms/messages/subscribe.tssrc/commands/rooms/occupancy/get.tssrc/commands/rooms/occupancy/subscribe.tssrc/commands/rooms/presence/enter.tssrc/commands/rooms/presence/subscribe.tssrc/commands/rooms/reactions/send.tssrc/commands/rooms/reactions/subscribe.tssrc/commands/rooms/typing/keystroke.tssrc/commands/rooms/typing/subscribe.tssrc/commands/spaces/cursors/get-all.tssrc/commands/spaces/cursors/set.tssrc/commands/spaces/cursors/subscribe.tssrc/commands/spaces/list.tssrc/commands/spaces/locations.tssrc/commands/spaces/locations/get-all.tssrc/commands/spaces/locations/set.tssrc/commands/spaces/locations/subscribe.tssrc/commands/spaces/locks/acquire.tssrc/commands/spaces/locks/get-all.tssrc/commands/spaces/locks/get.tssrc/commands/spaces/locks/subscribe.tssrc/commands/spaces/members/enter.tssrc/commands/spaces/members/subscribe.tssrc/commands/stats/account.tssrc/commands/stats/app.tssrc/commands/support/ask.tssrc/flags.tssrc/services/history-manager.tssrc/utils/time.tstest/e2e/interactive/ctrl-c-behavior.test.tstest/e2e/spaces/spaces-e2e.test.tstest/unit/commands/apps/set-apns-p12.test.tstest/unit/commands/channels/batch-publish.test.tstest/unit/commands/channels/history.test.tstest/unit/commands/did-you-mean.test.tstest/unit/commands/interactive-sigint.test.tstest/unit/commands/interactive.test.tstest/unit/commands/queues/delete.test.tstest/unit/commands/rooms/messages.test.tstest/unit/commands/stats/account.test.tstest/unit/commands/stats/app.test.tstest/unit/utils/time.test.ts
💤 Files with no reviewable changes (3)
- test/e2e/interactive/ctrl-c-behavior.test.ts
- test/e2e/spaces/spaces-e2e.test.ts
- src/commands/interactive.ts
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 81 out of 81 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
cf0db66 to
a84e04b
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 81 out of 81 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (5)
src/utils/time.ts:1
- The docstring says “ISO 8601 or any Date-parseable string”, but the implementation now rejects non-ISO strings via the
isIsoregex guard. Update the comment to match actual behavior (e.g., “ISO 8601 only”) or relax the validation to allow generalDate-parseable inputs.
src/utils/time.ts:1 - The regex permits date-times with no timezone (the
(Z|[+-]...)?is optional), whichnew Date()interprets in local time. That makesparseTimestamp("2023-01-01T00:00")machine/timezone-dependent and can lead to inconsistent CLI behavior. Consider requiring an explicit timezone on date-time inputs (keep date-only as-is), or normalize timezone-less inputs to UTC explicitly.
src/commands/queues/delete.ts:1 - In
--jsonmode with no--force, the command will skip confirmation and proceed with deletion. To avoid accidental destructive actions in non-interactive/scripted usage, consider requiring--forcewhenever confirmation cannot be shown (e.g.,--jsonor non-TTY), and emit a structured JSON error telling the user to pass--force.
src/commands/spaces/cursors/get-all.ts:1 - This changes cursor updates from an in-place terminal update (
process.stdout.write("\r...")) to line-by-line logging. For long-running subscriptions this can generate a lot of output (and slow down / bloat logs). IfshouldUseTerminalUpdates()still exists, consider restoring the in-place update for TTY (non-JSON) and falling back tothis.log()only for non-TTY or when explicitly requested.
test/unit/commands/stats/app.test.ts:1 - These tests validate that the command runs, but
.query(true)doesn’t assert that--start/--endare actually parsed and sent correctly (especially important for relative timestamps). Consider freezing time (fake timers) and asserting the exact expected query params (or at least thatstart/endexist and are numeric ms) so regressions inparseTimestamp()wiring are caught.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
a84e04b to
455c25d
Compare
455c25d to
ca4c3c7
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 81 out of 81 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Resolve merge conflicts in channel-rules create/delete (redundant appId assignment) - Fix history commands passing "Unknown timestamp" through formatTimestamp() - channels/history, logs/history, logs/connection-lifecycle/history, logs/push/history Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 18
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
src/commands/stats/app.ts (2)
177-184:⚠️ Potential issue | 🟠 MajorKeep the shutdown message out of JSON stdout.
Line 183 still prints plain text during
--live --json, so terminating the command breaks machine-readable output even though the new startup path is JSON-aware. Guard this log withshouldOutputJson()or send it to stderr instead.Suggested fix
const cleanup = () => { if (this.pollInterval) { clearInterval(this.pollInterval); this.pollInterval = undefined; } - this.log("\nUnsubscribed from live stats"); + if (!this.shouldOutputJson(flags)) { + this.log("\nUnsubscribed from live stats"); + } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/stats/app.ts` around lines 177 - 184, The cleanup() function prints a plain-text shutdown message via this.log which corrupts JSON-only output; change it to either call this.log only when shouldOutputJson() is false or send the message to stderr (e.g., use this.logError or process.stderr.write) so that when shouldOutputJson() is true no plain text is written. Update the block that clears this.pollInterval and the final this.log("\nUnsubscribed from live stats") to be conditional on shouldOutputJson() (or routed to stderr) to keep JSON stdout machine-readable.
37-44:⚠️ Potential issue | 🟠 Major
--liveaccepts--start/--endand then ignores them.After adding
...timeRangeFlags, this command will happily parse--start/--endin live mode, butfetchAndDisplayStats()always requests the last 24 hours. That makes the CLI silently disregard user input. Either reject those flags with--liveor thread them through the live fetch path.Suggested fix
if (flags.live && flags.unit !== "minute") { this.warn( "Live stats only support minute intervals. Using minute interval.", ); flags.unit = "minute"; } + + if (flags.live && (flags.start || flags.end)) { + this.error("--start/--end are not supported with --live"); + }Also applies to: 164-190
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/stats/app.ts` around lines 37 - 44, The live mode currently accepts --start/--end from timeRangeFlags but fetchAndDisplayStats() ignores them and always uses the last 24 hours; update the command so live mode either rejects start/end or respects them: inside the command's run() (or the flag-parsing path that handles the live boolean) add validation that if flags.live is true and either flags.start or flags.end is provided you either throw a usage error (fail fast) or pass those parsed start/end values into the live polling call, making fetchAndDisplayStats() (or the live fetch helper it calls) accept optional start/end parameters and use them instead of the default last-24-hours range when provided; reference the static flags (timeRangeFlags), the flags.live boolean, and fetchAndDisplayStats() to locate where to add validation or parameter threading.src/commands/stats/account.ts (2)
159-166:⚠️ Potential issue | 🟡 MinorCleanup message may pollute JSON output.
The cleanup handler logs
"Unsubscribed from live stats"unconditionally (line 165), which could interfere with JSON parsing when--jsonis used. Consider guarding this message.Proposed fix
const cleanup = () => { if (this.pollInterval) { clearInterval(this.pollInterval); this.pollInterval = undefined; } - - this.log("\nUnsubscribed from live stats"); + if (!this.shouldOutputJson(flags)) { + this.log("\nUnsubscribed from live stats"); + } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/stats/account.ts` around lines 159 - 166, The cleanup function unconditionally calls this.log("\nUnsubscribed from live stats"), which can corrupt machine-readable output; modify cleanup in the same block (the cleanup arrow function that clears this.pollInterval) to only emit that human-readable message when not running in JSON mode (check the command's JSON flag/property, e.g. this.json or a --json option) — otherwise suppress the log so only valid JSON remains.
197-207:⚠️ Potential issue | 🟡 MinorUnreachable cleanup code after
this.error().
this.error()throws an exception in oclif, so lines 204-206 are unreachable when not in JSON mode. Consider restructuring to ensure cleanup runs before the error:Proposed fix
} catch (error) { + if (this.pollInterval) { + clearInterval(this.pollInterval); + } const errorMsg = `Error setting up live stats: ${error instanceof Error ? error.message : String(error)}`; if (this.shouldOutputJson(flags)) { this.jsonError({ error: errorMsg, success: false }, flags); } else { this.error(errorMsg); } - if (this.pollInterval) { - clearInterval(this.pollInterval); - } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/stats/account.ts` around lines 197 - 207, The catch block calls this.error() which throws, making clearInterval(this.pollInterval) unreachable; move the cleanup before throwing or ensure cleanup always runs by clearing the interval at the top of the catch block (i.e., call if (this.pollInterval) clearInterval(this.pollInterval); before invoking this.jsonError or this.error in the catch inside the method where the try/catch currently lives), or alternatively capture the error, perform cleanup, then rethrow or call this.error; reference this.error, this.jsonError, and this.pollInterval when applying the change.src/commands/logs/history.ts (1)
103-109:⚠️ Potential issue | 🟡 MinorPotential issue:
formatTimestampmay receive a non-timestamp string.When
message.timestampis falsy,timestampis set to"Unknown timestamp"(line 105), but this string is then passed toformatTimestamp(line 108). Depending on howformatTimestamphandles non-date strings, this could produce unexpected output or errors.Consider guarding the call:
Proposed fix
- this.log( - `${chalk.dim(`[${index + 1}]`)} ${formatTimestamp(timestamp)}`, - ); + const formattedTime = timestamp === "Unknown timestamp" + ? chalk.dim("[Unknown timestamp]") + : formatTimestamp(timestamp); + this.log( + `${chalk.dim(`[${index + 1}]`)} ${formattedTime}`, + );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/logs/history.ts` around lines 103 - 109, The code sets timestamp to "Unknown timestamp" when message.timestamp is falsy but still passes that string into formatTimestamp; update the logic around the timestamp handling in the history command so formatTimestamp is only called with a real Date/ISO string (or a nullable value) — e.g., check message.timestamp (or the local timestamp variable) before invoking formatTimestamp and render the literal "Unknown timestamp" directly when absent; locate the timestamp assignment and the this.log call in the function handling message rendering (references: message.timestamp, timestamp variable, formatTimestamp) and implement the guard or conditional formatting there.src/commands/spaces/cursors/set.ts (3)
49-72:⚠️ Potential issue | 🟠 Major
--duration 0is inverted from the shared long-running command contract.The flag surface now uses the shared
-Dalias, but this command still treatsflags.duration === 0as “exit almost immediately” at Lines 394-401. Other long-running commands use0 = run indefinitely, so users will get the opposite behavior here unless the implementation and help text are aligned.As per coding guidelines, "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/cursors/set.ts` around lines 49 - 72, Update the duration flag help text and runtime handling so 0 means run indefinitely (consistent with other long-running commands): change the Flags.integer description for duration to "Automatically exit after N seconds (0 = run indefinitely)" (it already has char: "D"), and modify the logic that currently treats flags.duration === 0 as "exit immediately" (the check around flags.duration in this command's run loop / the block referenced at lines ~394-401) to instead treat 0 as no timeout (i.e., skip automatic exit when duration === 0) so behavior and help text are aligned.
110-153:⚠️ Potential issue | 🟠 MajorFinish the JSON error path for input validation.
These new
jsonError()branches only coverJSON.parse()failures. If the JSON parses butpositionis missing/typed wrong at Line 164, or no position input is supplied at Line 170, the command still falls back tothis.error(). That leaves--jsoncallers with mixed output formats for the same class of validation errors.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/cursors/set.ts` around lines 110 - 153, After parsing flags.data into cursorData (and after building cursorData from flags.x/flags.y plus parsed additionalData), validate that cursorData.position exists and that position.x and position.y are numbers; if validation fails or no position was supplied in the flags.data-only path, return the same JSON-formatted error when shouldOutputJson(flags) is true by calling this.jsonError({ error: <message>, success: false }, flags) and otherwise call this.error(<message>); update the code paths around the JSON.parse success in the handlers that set cursorData (referencing jsonError, shouldOutputJson, cursorData, flags, and this.error) to produce consistent JSON output for missing or incorrectly typed position data.
105-116:⚠️ Potential issue | 🟡 MinorReject non-object sidecar metadata before assigning
cursorData.data.
JSON.parse()accepts any JSON value here, but this command documents--dataas key/value cursor metadata. Inputs like--data 'false'or--data '0'are currently accepted and then dropped by the truthiness check at Line 259; arrays and strings are forwarded with the wrong shape. Please require a plain object before storing it underdata.Also applies to: 127-138
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/cursors/set.ts` around lines 105 - 116, The parsed --data value must be validated as a plain object before assigning to cursorData.data (and the other similar assignment block that sets cursorUpdate.data); after JSON.parse(flags.data) check that the result is !== null, typeof result === 'object' and !Array.isArray(result) and only then set cursorData.data (or cursorUpdate.data); if the check fails, reuse the existing error handling path (this.shouldOutputJson => this.jsonError(...) else this.error(...)) with the same "Invalid JSON in --data flag..." message so primitives, arrays, nulls and other non-object JSON are rejected.
🧹 Nitpick comments (10)
src/commands/logs/connection-lifecycle/subscribe.ts (2)
22-26: Duration flag description is incomplete per coding guidelines.The description should include the
(0 = run indefinitely)clarification.📝 Suggested fix
duration: Flags.integer({ - description: "Automatically exit after N seconds", + description: "Automatically exit after N seconds (0 = run indefinitely)", char: "D", required: false, }),As per coding guidelines: "The --duration flag should have description 'Automatically exit after N seconds (0 = run indefinitely)' with alias -D".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/logs/connection-lifecycle/subscribe.ts` around lines 22 - 26, The duration flag definition (duration: Flags.integer) has an incomplete description; update its description to "Automatically exit after N seconds (0 = run indefinitely)" and ensure the alias char remains "D" (alias -D) in the Flags.integer call so the flag matches coding guidelines; locate the duration flag in subscribe.ts and replace the current description string accordingly.
106-110: Secondary label should usechalk.dim()instead ofchalk.green().Per the PR objectives and coding guidelines, secondary labels like "Data:" should use
chalk.dim()rather thanchalk.green().📝 Suggested fix
if (message.data !== null && message.data !== undefined) { this.log( - `${chalk.green("Data:")} ${JSON.stringify(message.data, null, 2)}`, + `${chalk.dim("Data:")} ${JSON.stringify(message.data, null, 2)}`, ); }As per coding guidelines: "Use chalk.dim() for secondary labels like field names in structured output".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/logs/connection-lifecycle/subscribe.ts` around lines 106 - 110, The secondary label "Data:" in the message handling of the subscribe command is using chalk.green(); change it to chalk.dim() so secondary labels follow the project's style guide—locate the block that checks message.data !== null && message.data !== undefined inside the subscribe handler and replace chalk.green("Data:") with chalk.dim("Data:") in the this.log call that JSON.stringifies message.data.test/unit/commands/queues/delete.test.ts (1)
421-423: Consider updating skipped test expectations for consistency.These skipped tests still expect the old message format (
Queue "${mockQueueName}"and"deleted successfully"), while the active tests now expect"Queue deleted:". If these tests are ever unskipped, they would fail due to the format mismatch.💡 Suggested update
- expect(stdout).toContain(`Queue "${mockQueueName}"`); - expect(stdout).toContain("deleted successfully"); + expect(stdout).toContain("Queue deleted:");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/unit/commands/queues/delete.test.ts` around lines 421 - 423, The skipped assertions in delete.test.ts still expect the old output format ("Queue \"${mockQueueName}\"" and "deleted successfully") which now conflicts with active tests that assert "Queue deleted:"; update the skipped test expectations to match the new format by changing the expect calls that reference stdout and mockQueueName to assert the "Queue deleted:" style (use the same stdout contains checks as the active tests) so that if the tests are unskipped they will pass consistently with the current output formatting.src/commands/apps/set-apns-p12.ts (1)
57-59: Progress message should end with...Per coding guidelines, progress messages should end with
...to indicate an in-progress action.Suggested fix
this.log( - progress(`Uploading APNS P12 certificate for app ${resource(args.id)}`), + progress(`Uploading APNS P12 certificate for app ${resource(args.id)}...`), );As per coding guidelines: "Use progress() helper for in-progress actions (no color on action text, ends with '...')"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/apps/set-apns-p12.ts` around lines 57 - 59, The progress message logged by this.log(progress(...)) does not end with an ellipsis; update the string passed to progress in the set-apns-p12 command so it ends with "..." (e.g. change `Uploading APNS P12 certificate for app ${resource(args.id)}` to `Uploading APNS P12 certificate for app ${resource(args.id)}...`). Locate the call to progress(...) inside the command (the this.log(progress(...)) invocation) and append the three dots to the template literal so the progress helper output follows the coding guideline..claude/CLAUDE.md (1)
127-127: Consider expanding the relative time format examples.The documentation shows examples
"1h","30m","2d", but the PR summary indicates additional supported formats including"30s","5m", and"1w". Including a more comprehensive set of examples would help developers understand the full range of supported formats.📝 Suggested enhancement
-- **`timeRangeFlags`** — `--start`, `--end`. Use for history and stats commands. Parse with `parseTimestamp()` from `src/utils/time.ts`. Accepts ISO 8601, Unix ms, or relative (e.g., `"1h"`, `"30m"`, `"2d"`). +- **`timeRangeFlags`** — `--start`, `--end`. Use for history and stats commands. Parse with `parseTimestamp()` from `src/utils/time.ts`. Accepts ISO 8601, Unix ms, or relative (e.g., `"30s"`, `"5m"`, `"1h"`, `"2d"`, `"1w"`).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.claude/CLAUDE.md at line 127, Update the documentation for timeRangeFlags to show a more comprehensive set of relative time examples supported by parseTimestamp() in src/utils/time.ts (e.g., "30s", "1m", "5m", "30m", "1h", "1d", "2d", "1w") so readers clearly see seconds/minutes/hours/days/weeks are accepted; edit the line describing **`timeRangeFlags`** (the `--start`, `--end` examples) to include these additional relative formats.src/commands/integrations/delete.ts (1)
92-116: Consider usingchalk.dim()for secondary field labels.The JSON output structure looks good. For the non-JSON output, the coding guidelines recommend using
chalk.dim()for secondary labels like field names in structured output. The labels on lines 112-115 (ID:,App ID:, etc.) could use this styling for consistency with other commands.♻️ Optional: Apply chalk.dim() to field labels
Add chalk import:
+import chalk from "chalk"; import { Args, Flags } from "@oclif/core";Then update the output:
} else { this.log( success(`Integration rule deleted: ${resource(integration.id)}.`), ); - this.log(`ID: ${integration.id}`); - this.log(`App ID: ${integration.appId}`); - this.log(`Type: ${integration.ruleType}`); - this.log(`Source Type: ${integration.source.type}`); + this.log(`${chalk.dim("ID:")} ${integration.id}`); + this.log(`${chalk.dim("App ID:")} ${integration.appId}`); + this.log(`${chalk.dim("Type:")} ${integration.ruleType}`); + this.log(`${chalk.dim("Source Type:")} ${integration.source.type}`); }As per coding guidelines: "Use chalk.dim() for secondary labels like field names in structured output".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/integrations/delete.ts` around lines 92 - 116, The non-JSON output in the delete command prints field labels as plain text—wrap the secondary labels (the static parts in the this.log calls that print `ID:`, `App ID:`, `Type:`, `Source Type:`) with chalk.dim() for consistent styling; add an import for chalk at the top of the file and update the four this.log(...) lines in the else branch (the block guarded by this.shouldOutputJson(flags) and using success(), resource(), and formatJsonOutput) so each static label is formatted via chalk.dim() while keeping the dynamic values unchanged.src/commands/support/ask.ts (1)
38-43: Use the sharedprogress()helper here.This branch re-implements the in-progress UI with
oraplus a manualthis.log("Thinking..."), sosupport asknow diverges from the shared command UX the rest of this PR is standardizing. Routing this throughprogress("Thinking...")would keep the behavior consistent in one place.As per coding guidelines, "Use progress() helper for in-progress actions (no color on action text, ends with '...')".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/support/ask.ts` around lines 38 - 43, Replace the ad-hoc ora spinner and manual this.log("Thinking...") with the shared progress helper: remove the ora creation and the conditional this.log call in the support ask command and instead call progress("Thinking...") where the current spinner logic runs (respecting the same isInteractive / shouldOutputJson(flags) condition); ensure you import or reference the progress() helper and use its returned handle/stop behavior consistent with other commands so the in-progress UI matches the shared UX.test/unit/utils/time.test.ts (1)
83-88: Always restore fake timers in this test.If the assertion on Line 87 fails, Line 88 never runs and the suite keeps fake timers enabled. Wrap this setup in
try/finallyor move it into a nestedbeforeEach/afterEachblock for the whitespace suite.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/unit/utils/time.test.ts` around lines 83 - 88, The test "should trim whitespace from relative input" uses vi.useFakeTimers() but may leave fake timers enabled if the assertion throws; wrap the fake-timer setup and assertion in a try/finally (or move to a nested beforeEach/afterEach) so vi.useRealTimers() always runs. Specifically, in the test containing parseTimestamp(" 1h "), ensure vi.useRealTimers() is called in a finally block (or ensure the whitespace suite uses beforeEach to call vi.useFakeTimers() and afterEach to call vi.useRealTimers()) so timers are always restored even on assertion failure.src/commands/rooms/messages/history.ts (1)
67-69: Unreachable code afterthis.error().In oclif,
this.error()throws an exception and never returns, making thereturnstatement on line 68 unreachable. While harmless, it can be removed for clarity.Proposed fix
if (!chatClient) { this.error("Failed to create Chat client"); - return; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/rooms/messages/history.ts` around lines 67 - 69, The code calls this.error("Failed to create Chat client") which in oclif throws and never returns, so remove the redundant unreachable return statement immediately after it (in the same block where the Chat client is created), leaving only the this.error(...) call; locate the occurrence in the history command handler (the method containing the Chat client creation) and delete the trailing return to clean up unreachable code.src/commands/logs/push/history.ts (1)
2-2: Makeablya type-only import
Ablyis only used for a type annotation here. Switching this toimport typeavoids a needless runtime import under type-preserving TS emits and usually keepsconsistent-type-imports-style lint rules happy.♻️ Proposed fix
-import * as Ably from "ably"; +import type * as Ably from "ably";As per coding guidelines, "Ensure all code passes ESLint linter checks using
pnpm exec eslint."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/logs/push/history.ts` at line 2, Replace the runtime import of Ably with a type-only import since the symbol Ably is used only for type annotations; change the import statement that currently reads `import * as Ably from "ably";` to a type-only import (e.g., using `import type`) so the module is not included at runtime and ESLint consistent-type-imports rules are satisfied, then run the linter to verify (refer to the Ably symbol in this file when making the change).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/commands/channels/history.ts`:
- Around line 80-87: Reject inverted time ranges by validating that when both
flags.start and flags.end are present (after converting with parseTimestamp),
the resulting values produce start <= end; if start > end throw or return a
clear error before calling channel.history(). Update the block that sets
historyParams (using parseTimestamp and historyParams.start/historyParams.end)
to perform this check and surface a user-friendly message referencing the flags
(e.g., "start must be earlier than or equal to end") instead of proceeding to
channel.history().
In `@src/commands/channels/presence/enter.ts`:
- Around line 43-47: Update the duration flag's description on the Flags.integer
configuration for the duration option so it reads "Automatically exit after N
seconds (0 = run indefinitely)"; locate the duration flag definition (the
duration: Flags.integer({...}) object) in enter.ts and change only the
description string to append " (0 = run indefinitely)".
In `@src/commands/logs/channel-lifecycle/subscribe.ts`:
- Around line 27-31: Update the duration flag definition in subscribe.ts (the
duration: Flags.integer({...}) entry) to use the repo-standard help text
exactly: set description to "Automatically exit after N seconds (0 = run
indefinitely)" and keep the alias char "D" so the flag reads and behaves
consistently with other long-running commands.
In `@src/commands/logs/push/history.ts`:
- Around line 57-64: Parse the relative start/end inputs before awaiting
createAblyRestClient so the timestamps reflect the CLI invocation time;
specifically call parseTimestamp on flags.start and flags.end prior to the await
this.createAblyRestClient(flags), store the parsed values (e.g., parsedStart,
parsedEnd), and then assign those to historyOptions.start/historyOptions.end
when building historyOptions later (instead of calling parseTimestamp after the
await). Ensure you keep using the same parsed variables and still validate
presence of flags.start/flags.end.
In `@src/commands/logs/push/subscribe.ts`:
- Around line 27-31: Update the duration flag definition in subscribe.ts (the
Flags.integer call for the "duration" flag) to use the standard description
"Automatically exit after N seconds (0 = run indefinitely)" and ensure the alias
char remains "D" so it matches the rest of the CLI surface and guidelines.
In `@src/commands/rooms/occupancy/get.ts`:
- Line 4: The new import of productApiFlags removed the client-id flag; restore
preservation of the client identity by adding the existing clientIdFlag back
into this command's flag surface alongside productApiFlags (i.e., merge
clientIdFlag with productApiFlags in the command's flags definition in get.ts)
so that --client-id remains supported for Chat realtime room attachment flows;
reference the clientIdFlag symbol and productApiFlags symbol when making the
change.
In `@src/commands/rooms/presence/enter.ts`:
- Around line 46-49: Restore the standard help text for the duration flag:
update the Flags.integer definition for the duration flag (the `duration`
symbol) so its description string is "Automatically exit after N seconds (0 =
run indefinitely)" and ensure the alias remains char: "D" (i.e., keep -D). This
ensures the CLI help accurately documents the 0 = run indefinitely behavior for
the duration flag.
In `@src/commands/rooms/presence/subscribe.ts`:
- Around line 44-46: The duration flag description was shortened; update the
Flags.integer call for the duration flag (symbol: duration) to use the standard
text "Automatically exit after N seconds (0 = run indefinitely)" and ensure the
char alias is 'D' (alias -D) so the CLI help matches shared wording and
documents indefinite-run behavior.
In `@src/commands/rooms/reactions/subscribe.ts`:
- Line 6: The command replaced an explicit client-id flag by importing
productApiFlags, removing the --client-id option; restore the clientIdFlag into
the command's flags so realtime commands (like the subscribe command that
imports productApiFlags) accept --client-id again by merging or adding
clientIdFlag alongside productApiFlags (ensure the flags object used by the
command includes both productApiFlags and clientIdFlag symbols so the realtime
connection can receive an explicit client identity).
- Around line 34-36: The duration flag's help text was changed and no longer
mentions that 0 runs indefinitely; update the description for the duration
Flags.integer declaration (the duration flag configured with Flags.integer and
char: "D") to read exactly "Automatically exit after N seconds (0 = run
indefinitely)" and ensure the alias remains -D so --help shows the standard
behavior.
In `@src/commands/rooms/typing/keystroke.ts`:
- Around line 43-47: Update the duration flag definition for the `duration`
Flags.integer entry in keystroke.ts to match the shared long-running flag
contract: keep the alias char 'D' (short flag -D) and change the description to
"Automatically exit after N seconds (0 = run indefinitely)". Locate the
`duration` Flags.integer block and replace the existing description string with
the standardized one so this command is consistent with other long-running
commands.
- Line 38: Add the client identity flag to this typing keystroke command by
including ...clientIdFlag alongside ...productApiFlags in the command flags list
(so locate where productApiFlags is spread and add ...clientIdFlag); also update
the duration flag's description (the duration flag constant or flags entry named
duration) to read "Automatically exit after N seconds (0 = run indefinitely)".
Ensure you only modify the flags object for the keystroke/send command
(referencing productApiFlags, clientIdFlag, and duration).
In `@src/commands/spaces/locations/get-all.ts`:
- Around line 162-181: The extractLocationData helper is incorrectly treating
current/member fields as data, causing locations with the current.member shape
to include wrapper metadata and lose memberId; update knownMetaKeys to include
"current" and "member" (and possibly "memberId" already present) and adjust the
JSON-branch logic in extractLocationData (and the equivalent blocks at the other
occurrences) to explicitly check for member.memberId or current.member and, when
present, prefer returning that nested member/location value or null instead of
the whole object so memberId extraction later does not fall back to "Unknown".
In `@src/commands/spaces/members/enter.ts`:
- Around line 40-44: The duration flag definition uses a nonstandard
description; update the Flags.integer call for the duration flag (the object
with key duration, char "D") to use the repository-standard help text exactly:
"Automatically exit after N seconds (0 = run indefinitely)"; keep the alias char
"D" and required: false unchanged so the flag matches other long-running
commands.
In `@src/commands/spaces/members/subscribe.ts`:
- Around line 31-35: The --duration flag description in the duration Flag
definition inside src/commands/spaces/members/subscribe.ts should match the
shared wording; update the description for the duration Flag (the object
assigned to duration in the Flags.integer call in the subscribe command) to
"Automatically exit after N seconds (0 = run indefinitely)" and ensure the flag
char is "D" (alias -D) to maintain consistency with other long-running commands.
In `@src/commands/stats/account.ts`:
- Around line 224-247: The code currently leaves startMs or endMs undefined when
only one of --start or --end is provided; update the logic after parseTimestamp
so that if only startMs is set, set endMs = startMs + 24*60*60*1000, and if only
endMs is set, set startMs = endMs - 24*60*60*1000, before calling
controlApi.getAccountStats; reference variables/functions: startMs, endMs,
parseTimestamp, flags.start, flags.end, controlApi.getAccountStats.
In `@src/commands/support/ask.ts`:
- Around line 90-111: The fenced code blocks in response.answer are being
post-processed by inline markdown replacements (the current
processedWithCodeBlocks approach), causing backticks/asterisks inside fences to
be mangled; fix it by extracting fenced blocks first into an array (e.g., create
codeBlocks: string[]), replace each fence in response.answer with a unique
placeholder (use a regex like /```[^\n]*\n([\S\s]*?)```/g), store the
highlighted/chalked code block strings in codeBlocks, then run the existing
.replaceAll(...) chain on the placeholder-containing string (use
withoutCodeBlocks instead of processedWithCodeBlocks) and finally restore blocks
by replacing placeholders (e.g., /__CODE_BLOCK_(\d+)__/g) with
codeBlocks[Number(index)] when building formattedAnswer.
In `@test/unit/commands/channels/batch-publish.test.ts`:
- Around line 261-268: The current assertions check fragments separately,
allowing labels to be misassociated; update the test to assert each rendered
item as a whole by matching related fragments together (e.g., for the success
case ensure a single line contains "Published to channel", "channel1", and
"msg-1", and for the failure case ensure a single line contains "Failed to
publish to channel", "channel2", and the error "Invalid channel name (40000)").
Locate the assertions that reference stdout, channel1, channel2, msg-1 and
Invalid channel name (40000) and replace the loose expect(...toContain...) calls
with either a single regex match against stdout (e.g., /Published to
channel.*channel1.*msg-1/) or split stdout into lines and assert a specific line
includes all expected substrings for each item.
---
Outside diff comments:
In `@src/commands/logs/history.ts`:
- Around line 103-109: The code sets timestamp to "Unknown timestamp" when
message.timestamp is falsy but still passes that string into formatTimestamp;
update the logic around the timestamp handling in the history command so
formatTimestamp is only called with a real Date/ISO string (or a nullable value)
— e.g., check message.timestamp (or the local timestamp variable) before
invoking formatTimestamp and render the literal "Unknown timestamp" directly
when absent; locate the timestamp assignment and the this.log call in the
function handling message rendering (references: message.timestamp, timestamp
variable, formatTimestamp) and implement the guard or conditional formatting
there.
In `@src/commands/spaces/cursors/set.ts`:
- Around line 49-72: Update the duration flag help text and runtime handling so
0 means run indefinitely (consistent with other long-running commands): change
the Flags.integer description for duration to "Automatically exit after N
seconds (0 = run indefinitely)" (it already has char: "D"), and modify the logic
that currently treats flags.duration === 0 as "exit immediately" (the check
around flags.duration in this command's run loop / the block referenced at lines
~394-401) to instead treat 0 as no timeout (i.e., skip automatic exit when
duration === 0) so behavior and help text are aligned.
- Around line 110-153: After parsing flags.data into cursorData (and after
building cursorData from flags.x/flags.y plus parsed additionalData), validate
that cursorData.position exists and that position.x and position.y are numbers;
if validation fails or no position was supplied in the flags.data-only path,
return the same JSON-formatted error when shouldOutputJson(flags) is true by
calling this.jsonError({ error: <message>, success: false }, flags) and
otherwise call this.error(<message>); update the code paths around the
JSON.parse success in the handlers that set cursorData (referencing jsonError,
shouldOutputJson, cursorData, flags, and this.error) to produce consistent JSON
output for missing or incorrectly typed position data.
- Around line 105-116: The parsed --data value must be validated as a plain
object before assigning to cursorData.data (and the other similar assignment
block that sets cursorUpdate.data); after JSON.parse(flags.data) check that the
result is !== null, typeof result === 'object' and !Array.isArray(result) and
only then set cursorData.data (or cursorUpdate.data); if the check fails, reuse
the existing error handling path (this.shouldOutputJson => this.jsonError(...)
else this.error(...)) with the same "Invalid JSON in --data flag..." message so
primitives, arrays, nulls and other non-object JSON are rejected.
In `@src/commands/stats/account.ts`:
- Around line 159-166: The cleanup function unconditionally calls
this.log("\nUnsubscribed from live stats"), which can corrupt machine-readable
output; modify cleanup in the same block (the cleanup arrow function that clears
this.pollInterval) to only emit that human-readable message when not running in
JSON mode (check the command's JSON flag/property, e.g. this.json or a --json
option) — otherwise suppress the log so only valid JSON remains.
- Around line 197-207: The catch block calls this.error() which throws, making
clearInterval(this.pollInterval) unreachable; move the cleanup before throwing
or ensure cleanup always runs by clearing the interval at the top of the catch
block (i.e., call if (this.pollInterval) clearInterval(this.pollInterval);
before invoking this.jsonError or this.error in the catch inside the method
where the try/catch currently lives), or alternatively capture the error,
perform cleanup, then rethrow or call this.error; reference this.error,
this.jsonError, and this.pollInterval when applying the change.
In `@src/commands/stats/app.ts`:
- Around line 177-184: The cleanup() function prints a plain-text shutdown
message via this.log which corrupts JSON-only output; change it to either call
this.log only when shouldOutputJson() is false or send the message to stderr
(e.g., use this.logError or process.stderr.write) so that when
shouldOutputJson() is true no plain text is written. Update the block that
clears this.pollInterval and the final this.log("\nUnsubscribed from live
stats") to be conditional on shouldOutputJson() (or routed to stderr) to keep
JSON stdout machine-readable.
- Around line 37-44: The live mode currently accepts --start/--end from
timeRangeFlags but fetchAndDisplayStats() ignores them and always uses the last
24 hours; update the command so live mode either rejects start/end or respects
them: inside the command's run() (or the flag-parsing path that handles the live
boolean) add validation that if flags.live is true and either flags.start or
flags.end is provided you either throw a usage error (fail fast) or pass those
parsed start/end values into the live polling call, making
fetchAndDisplayStats() (or the live fetch helper it calls) accept optional
start/end parameters and use them instead of the default last-24-hours range
when provided; reference the static flags (timeRangeFlags), the flags.live
boolean, and fetchAndDisplayStats() to locate where to add validation or
parameter threading.
---
Nitpick comments:
In @.claude/CLAUDE.md:
- Line 127: Update the documentation for timeRangeFlags to show a more
comprehensive set of relative time examples supported by parseTimestamp() in
src/utils/time.ts (e.g., "30s", "1m", "5m", "30m", "1h", "1d", "2d", "1w") so
readers clearly see seconds/minutes/hours/days/weeks are accepted; edit the line
describing **`timeRangeFlags`** (the `--start`, `--end` examples) to include
these additional relative formats.
In `@src/commands/apps/set-apns-p12.ts`:
- Around line 57-59: The progress message logged by this.log(progress(...)) does
not end with an ellipsis; update the string passed to progress in the
set-apns-p12 command so it ends with "..." (e.g. change `Uploading APNS P12
certificate for app ${resource(args.id)}` to `Uploading APNS P12 certificate for
app ${resource(args.id)}...`). Locate the call to progress(...) inside the
command (the this.log(progress(...)) invocation) and append the three dots to
the template literal so the progress helper output follows the coding guideline.
In `@src/commands/integrations/delete.ts`:
- Around line 92-116: The non-JSON output in the delete command prints field
labels as plain text—wrap the secondary labels (the static parts in the this.log
calls that print `ID:`, `App ID:`, `Type:`, `Source Type:`) with chalk.dim() for
consistent styling; add an import for chalk at the top of the file and update
the four this.log(...) lines in the else branch (the block guarded by
this.shouldOutputJson(flags) and using success(), resource(), and
formatJsonOutput) so each static label is formatted via chalk.dim() while
keeping the dynamic values unchanged.
In `@src/commands/logs/connection-lifecycle/subscribe.ts`:
- Around line 22-26: The duration flag definition (duration: Flags.integer) has
an incomplete description; update its description to "Automatically exit after N
seconds (0 = run indefinitely)" and ensure the alias char remains "D" (alias -D)
in the Flags.integer call so the flag matches coding guidelines; locate the
duration flag in subscribe.ts and replace the current description string
accordingly.
- Around line 106-110: The secondary label "Data:" in the message handling of
the subscribe command is using chalk.green(); change it to chalk.dim() so
secondary labels follow the project's style guide—locate the block that checks
message.data !== null && message.data !== undefined inside the subscribe handler
and replace chalk.green("Data:") with chalk.dim("Data:") in the this.log call
that JSON.stringifies message.data.
In `@src/commands/logs/push/history.ts`:
- Line 2: Replace the runtime import of Ably with a type-only import since the
symbol Ably is used only for type annotations; change the import statement that
currently reads `import * as Ably from "ably";` to a type-only import (e.g.,
using `import type`) so the module is not included at runtime and ESLint
consistent-type-imports rules are satisfied, then run the linter to verify
(refer to the Ably symbol in this file when making the change).
In `@src/commands/rooms/messages/history.ts`:
- Around line 67-69: The code calls this.error("Failed to create Chat client")
which in oclif throws and never returns, so remove the redundant unreachable
return statement immediately after it (in the same block where the Chat client
is created), leaving only the this.error(...) call; locate the occurrence in the
history command handler (the method containing the Chat client creation) and
delete the trailing return to clean up unreachable code.
In `@src/commands/support/ask.ts`:
- Around line 38-43: Replace the ad-hoc ora spinner and manual
this.log("Thinking...") with the shared progress helper: remove the ora creation
and the conditional this.log call in the support ask command and instead call
progress("Thinking...") where the current spinner logic runs (respecting the
same isInteractive / shouldOutputJson(flags) condition); ensure you import or
reference the progress() helper and use its returned handle/stop behavior
consistent with other commands so the in-progress UI matches the shared UX.
In `@test/unit/commands/queues/delete.test.ts`:
- Around line 421-423: The skipped assertions in delete.test.ts still expect the
old output format ("Queue \"${mockQueueName}\"" and "deleted successfully")
which now conflicts with active tests that assert "Queue deleted:"; update the
skipped test expectations to match the new format by changing the expect calls
that reference stdout and mockQueueName to assert the "Queue deleted:" style
(use the same stdout contains checks as the active tests) so that if the tests
are unskipped they will pass consistently with the current output formatting.
In `@test/unit/utils/time.test.ts`:
- Around line 83-88: The test "should trim whitespace from relative input" uses
vi.useFakeTimers() but may leave fake timers enabled if the assertion throws;
wrap the fake-timer setup and assertion in a try/finally (or move to a nested
beforeEach/afterEach) so vi.useRealTimers() always runs. Specifically, in the
test containing parseTimestamp(" 1h "), ensure vi.useRealTimers() is called in a
finally block (or ensure the whitespace suite uses beforeEach to call
vi.useFakeTimers() and afterEach to call vi.useRealTimers()) so timers are
always restored even on assertion failure.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b8786d4c-3330-4fbe-b856-32088439d9b3
📒 Files selected for processing (81)
.claude/CLAUDE.md.cursor/rules/AI-Assistance.mdceslint.config.jssrc/commands/accounts/login.tssrc/commands/apps/channel-rules/create.tssrc/commands/apps/channel-rules/delete.tssrc/commands/apps/channel-rules/list.tssrc/commands/apps/create.tssrc/commands/apps/delete.tssrc/commands/apps/set-apns-p12.tssrc/commands/apps/update.tssrc/commands/auth/keys/create.tssrc/commands/bench/subscriber.tssrc/commands/channels/batch-publish.tssrc/commands/channels/history.tssrc/commands/channels/list.tssrc/commands/channels/occupancy.tssrc/commands/channels/occupancy/get.tssrc/commands/channels/occupancy/subscribe.tssrc/commands/channels/presence/enter.tssrc/commands/channels/presence/subscribe.tssrc/commands/channels/publish.tssrc/commands/channels/subscribe.tssrc/commands/integrations/delete.tssrc/commands/interactive.tssrc/commands/logs/channel-lifecycle/subscribe.tssrc/commands/logs/connection-lifecycle/history.tssrc/commands/logs/connection-lifecycle/subscribe.tssrc/commands/logs/history.tssrc/commands/logs/push/history.tssrc/commands/logs/push/subscribe.tssrc/commands/logs/subscribe.tssrc/commands/queues/delete.tssrc/commands/rooms/list.tssrc/commands/rooms/messages/history.tssrc/commands/rooms/messages/reactions/remove.tssrc/commands/rooms/messages/reactions/send.tssrc/commands/rooms/messages/reactions/subscribe.tssrc/commands/rooms/messages/send.tssrc/commands/rooms/messages/subscribe.tssrc/commands/rooms/occupancy/get.tssrc/commands/rooms/occupancy/subscribe.tssrc/commands/rooms/presence/enter.tssrc/commands/rooms/presence/subscribe.tssrc/commands/rooms/reactions/send.tssrc/commands/rooms/reactions/subscribe.tssrc/commands/rooms/typing/keystroke.tssrc/commands/rooms/typing/subscribe.tssrc/commands/spaces/cursors/get-all.tssrc/commands/spaces/cursors/set.tssrc/commands/spaces/cursors/subscribe.tssrc/commands/spaces/list.tssrc/commands/spaces/locations.tssrc/commands/spaces/locations/get-all.tssrc/commands/spaces/locations/set.tssrc/commands/spaces/locations/subscribe.tssrc/commands/spaces/locks/acquire.tssrc/commands/spaces/locks/get-all.tssrc/commands/spaces/locks/get.tssrc/commands/spaces/locks/subscribe.tssrc/commands/spaces/members/enter.tssrc/commands/spaces/members/subscribe.tssrc/commands/stats/account.tssrc/commands/stats/app.tssrc/commands/support/ask.tssrc/flags.tssrc/services/history-manager.tssrc/utils/time.tstest/e2e/interactive/ctrl-c-behavior.test.tstest/e2e/spaces/spaces-e2e.test.tstest/unit/commands/apps/set-apns-p12.test.tstest/unit/commands/channels/batch-publish.test.tstest/unit/commands/channels/history.test.tstest/unit/commands/did-you-mean.test.tstest/unit/commands/interactive-sigint.test.tstest/unit/commands/interactive.test.tstest/unit/commands/queues/delete.test.tstest/unit/commands/rooms/messages.test.tstest/unit/commands/stats/account.test.tstest/unit/commands/stats/app.test.tstest/unit/utils/time.test.ts
💤 Files with no reviewable changes (3)
- src/commands/interactive.ts
- test/e2e/spaces/spaces-e2e.test.ts
- test/e2e/interactive/ctrl-c-behavior.test.ts
🚧 Files skipped from review as they are similar to previous changes (38)
- src/commands/channels/subscribe.ts
- src/commands/apps/channel-rules/list.ts
- src/commands/channels/occupancy.ts
- src/commands/logs/connection-lifecycle/history.ts
- src/commands/spaces/locations.ts
- src/commands/apps/channel-rules/delete.ts
- src/commands/channels/list.ts
- .cursor/rules/AI-Assistance.mdc
- src/commands/rooms/messages/reactions/send.ts
- src/commands/rooms/occupancy/subscribe.ts
- src/commands/apps/channel-rules/create.ts
- src/commands/queues/delete.ts
- src/commands/rooms/messages/reactions/remove.ts
- src/commands/channels/batch-publish.ts
- src/commands/rooms/messages/reactions/subscribe.ts
- src/commands/rooms/typing/subscribe.ts
- src/commands/spaces/locks/get.ts
- src/commands/rooms/messages/subscribe.ts
- src/commands/spaces/locks/subscribe.ts
- src/commands/apps/delete.ts
- src/commands/apps/update.ts
- test/unit/commands/did-you-mean.test.ts
- test/unit/commands/interactive-sigint.test.ts
- src/commands/spaces/locations/subscribe.ts
- src/commands/logs/subscribe.ts
- src/commands/bench/subscriber.ts
- src/commands/spaces/locks/acquire.ts
- src/flags.ts
- src/utils/time.ts
- src/commands/spaces/cursors/subscribe.ts
- src/commands/channels/occupancy/subscribe.ts
- src/commands/channels/presence/subscribe.ts
- src/commands/spaces/locks/get-all.ts
- test/unit/commands/channels/history.test.ts
- src/commands/channels/publish.ts
- src/commands/spaces/list.ts
- src/commands/rooms/messages/send.ts
- src/commands/apps/create.ts
- Add inverted time range validation (--start > --end) to all history commands - Add clientIdFlag to rooms/occupancy/get, rooms/reactions/subscribe, rooms/typing/keystroke - Fix current.member serialization in spaces/locations/get-all JSON output - Handle partial time ranges in stats/account (default missing bound) - Fix misleading comments in interactive test and cursors/get-all Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1aef9d2 to
8083e60
Compare
Summary
Three related improvements across the CLI. The diff is large (~80 files) but most changes are mechanical.
Unified time range flags
History and stats commands each had their own --start/--end with inconsistent types. Now they all share timeRangeFlags from src/flags.ts and parse with parseTimestamp() from src/utils/time.ts, which accepts:
--start "2023-01-01T00:00:00Z" # ISO 8601
--start 1700000000000 # Unix ms
--start 1h # Relative (also: 30s, 5m, 2d, 1w)
Applied to: channels history, rooms messages history, logs history, logs connection-lifecycle history, logs push history, stats account, stats app.
Output consistency
Mechanical pass applying progress(), success(), resource() helpers everywhere they were missing. Also wrapped human output in shouldOutputJson() guards, switched chalk.green("Label:") to chalk.dim("Label:") for secondary labels, and ensured JSON mode emits structured errors instead of silently swallowing them.
Cleanup
Some of this stuff should've been added in previous PRs but Claude is getting better at picking up patterns and finding these issues. I created an Agent Team with several agents to go through and find these so hopefully this should be the baseline now.
Summary by CodeRabbit
New Features
Improvements